3W - 실리움 기본 - Routing, Masq, IP Frag
개요
이 노트도 이전 노트 내용에 이어 진행한다.
여기에서는 다음의 세 가지 내용을 다룬다.
- routing
- masquerading
- ip fragment handling
Routing
실리움은 클러스터 내 파드 간 네트워킹을 전담하는 CNI로서 파드 IP로 통신이 가능하도록 트래픽을 적절히 라우팅해준다.
다른 노드 간 트래픽을 주고받기 위해 실리움은 두 가지 모드를 제공한다.[1]
캡슐 모드와 네이티브 라우팅 모드인데, 이름은 여기저기 다르지만 결국 뜻은 같다.
캡슐화(encapsulation) 모드
기본 설정으로, 파드의 트래픽을 말 그대로 터널을 뚫어서(캡슐화하여) 보낸다.
네트워크 인프라 단의 별도의 설정 없이 동작할 수 있는 모드이다.
이 모드에서는 UDP 기반의 캡슐 터널링 프로토콜이 사용된다.
VXLAN, Geneve 두 가지 프로토콜을 사용할 수 있다.
- VXLAN - Virtual eXtensible Local Area Network
- VLAN(Virtual Local Area Network)의 한계를 극복하기 위해 등장
- VLAN은 말 그대로 가상의 LAN을 만드는 기술로 하나의 스위치로 여러 브로드캐스트 도메인을 나누기 위해 등장했다.
- 보통 내부망을 구성할 때 사용되나, 물리적으로 멀거나 대규모의 환경에서 사용하는 데에는 제약이 있다.
- L2 이더넷 프레임을 L3 UDP 패킷으로 캡슐화(Encapsulation)하여 기존 IP 네트워크 위에서 터널링
- 구조
- VTEP (VXLAN Tunnel End Point) - VXLAN 터널의 시작점과 끝점 역할을 하는 장치(주로 물리 스위치 또는 서버의 가상 스위치)
- VNI (VXLAN Network Identifier) - 각 VXLAN 가상 네트워크를 고유하게 식별하는 24비트 식별자
- 약 1,600만 개의 논리적 네트워크 세그먼트를 생성할 수 있어 VLAN의 4096개 한계 초월!
- 과정
- 원본 L2 이더넷 프레임이 VTEP에 도착
- VTEP이 VXLAN 헤더(VNI 포함) 추가
- UDP 헤더(기본 포트 4789)와 외부 IP 헤더(Source IP: 송신 VTEP, Destination IP: 수신 VTEP) 추가
- 캡슐화된 IP 패킷 언더레이 물리 네트워크를 통해 목적지 VTEP로 전달
- 목적지 VTEP는 패킷에서 외부 IP, UDP, VXLAN 헤더를 제거(디캡슐화)
- 원래의 Layer 2 이더넷 프레임을 목적지 호스트에게 전달
- VLAN(Virtual Local Area Network)의 한계를 극복하기 위해 등장
- Geneve - Generic Network Virtualization Encapsulation
- VXLAN 및 NVGRE(Network Virtualization using Generic Routing Encapsulation)와 같은 기존 오버레이 프로토콜의 장점을 결합, 확장성 있게 설계된 프로토콜
- 과정 자체는 비슷하며, TLV(Type-Length-Value) 가변 길이 필드를 붙여 여러 옵션 데이터를 추가할 수 있음
이 방식의 장점은 다음과 같다.
- 단순함 - 별도의 설정이 요구되지 않는다.
- vxlan에서 8472, geneve에서 6081 udp 포트만 모든 노드가 오픈하고 있으면 된다.
- 파드나 노드의 cidr을 일절 신경쓸 필요가 없다.
- 주소 공간 확보 - 별도의 캡슐링을 하니 다른 주소를 침범하지 않는다.
- 신원 컨텍스트 - 캡슐화 시 신원 메타데이터를 추가하기에 서비스간 신원을 식별하여 트래픽 전달이 최적화된다.
단점으로는 MTU 부하가 존재할 수 있다.
아무래도 패킷의 양이 늘어나고 자체 크기도 커질 수 있으며, 이는 처리량의 저하를 유발할 수 있다.
점보 프레임을 활성화해 이를 완화할 수는 있다.
이 부분은 다음에 별도로 실습을 진행하겠다.
네이티브 라우팅(native routing) 모드
routing-mode: native
를 통해 활성화할 수 있는 모드로, 노드의 라우팅 테이블을 활용한다.
이전에 간단하게 노드의 환경에서는 각 파드로 향하는 인터페이스가 lxc로 보이는 것을 확인했다.
이 모드에서는 자신이 가진 파드에 대해서는 /32로 정확하게 해당 인터페이스로 트래픽을 보내고, 다른 노드의 IP 대역에 대해서는 해당 노드의 IP로 트래픽을 보내도록 라우팅 테이블을 설정한다.
그래서 내부로 들어가는 주소만 아니라면 모든 패킷을 라우팅 테이블에 의해 라우팅되며, 패킷은 로컬 프로세스가 보내는 패킷처럼 다뤄진다.
패킷 포워딩 커널 파라미터가 설정돼야 하는데 이 모드를 설정하면 실리움이 알아서 세팅도 해준다. 위험한 놈
패킷을 캡슐화하지 않고 그대로 보내니 캡슐화하는데 들어가는 자원 소모는 없고 상대적으로 빠른 속도가 보장된다.
다른 CNI 프로바이더들은 대체로 캡슐화 방식을 사용하는데 반해 실리움이 가진 강점 중 하나라 할 수 있겠다.
참고로 라우팅 테이블을 사용한다는 것은 노드 별로 다른 PodCIDR이 있기에 가능한 것이다.
그런 만큼 몇 가지 제약이 있다.
- 자신 노드가 아닌 파드 주소에 대한 라우팅 시 대상 노드가 트래픽을 받을 수 있어야 함
- 노드 자신이 라우팅 경로를 알 수 없다면 별도의 라우터가 요구된다.
- 이 경우에는 모든 트래픽을 일단 라우터로 날리고 라우터 구체적으로 트래픽을 분할하게 될 것이다.
- 또는 각 노드가 전부 온전히 모든 노드가 가진 PodCIDR를 아는 상태로 라우팅 테이블에 반영해야 한다.
- 모든 노드가 L2에서 공유된다면,
auto-direct-node-routes: true
로 가능하다. - 그렇지 않다면 BGP로 각 주소를 전파해줘야만 한다.
- 모든 노드가 L2에서 공유된다면,
- 노드 자신이 라우팅 경로를 알 수 없다면 별도의 라우터가 요구된다.
실습
초기 클러스터 구축 시 네이티브 라우팅으로 세팅했기 때문에 상태를 확인하고 트래픽을 뜯어보는 정도만 하고자 한다.
라우팅 테이블은 단순하게 보기만 하면 된다.
vagrant ssh k8s-ctr -c 'ip -c route'
vagrant ssh k8s-w1 -c 'ip -c route'
보다시피 파드가 생긴 만큼 이에 해당하는 인터페이스로 빠지라는 경로 규칙이 설정된 것을 볼 수 있다.
또한 각 테이블에서는 서로의 노드 IP 대역에 대해 각 노드로 트래픽을 보내라는 규칙도 작성되어 있다.
이를 통해 클러스터 IP 대역의 트래픽이 발생했을 때 성공적으로 각 노드로 가게 될 것이란 것을 보장할 수 있다.
그렇다면 실제로 트래픽을 보내면서 패킷도 간단하게 확인해보자.
export WEBPODIP1=$(kubectl get -l app=webpod pods --field-selector spec.nodeName=k8s-ctr -o jsonpath='{.items[0].status.podIP}')
export WEBPODIP2=$(kubectl get -l app=webpod pods --field-selector spec.nodeName=k8s-w1 -o jsonpath='{.items[0].status.podIP}')
kubectl exec -it curl-pod -- ping $WEBPODIP2
hubble observe -f --pod curl-pod --ip-translation=false
# 여기는 vm 들어가서 확인
tcpdump -i any icmp -w /tmp/icmp.pcap
termshark -r /tmp/icmp.pcap
허블로 볼 때 신원이 아니라 주소로 확인할 때는 위와 같이 옵션을 주면 된다.
아무튼 IP에 대한 일체의 변경 없이 트래픽이 전달되고 있는 것을 볼 수 있다.
any로 걸어서 트래픽을 뜯어보면 다음과 같이 4개의 덤프가 하나의 ping 요청 응답을 이룬다.
- lxc 인터페이스에서 나온 패킷
- 을 받아서 eth1을 통해 나가는 패킷
- 응답으로 eth1을 통해 들어온 패킷
- 을 받아서 lxc 인터페이스로 들어가는 패킷
각각의 인터페이스 번호를 확인해보면 이것을 조금 더 명확하게 볼 수 있다.
내 경우엔 21번 인터페이스와 3번 인터페이스가 사용됐는데, 파드의 인터페이스가 21번이고 실제로 트래픽이 나갈 때 사용되는 eth1이 3번 인터페이스였다.
Masquerading
마스커레이딩이란
마스커레이딩은 간단하게 말하자면 NAT이다.
조금 더 구체적으로는 LAN에서 외부로 나가는 트래픽을 위해 공인 IP로 소스 IP를 바꾸는 작업을 마스커레이딩이라 표현한다.[2]
처음에는 왜 굳이 이렇게 부르는가 했는데, 하나의 이그레스 게이트웨이에서 여러 노드의 트래픽이 나갈 때 다시 각 노드로 응답이 잘 돌아가게끔 적절하게 트래픽의 출처를 기록하고 관리해야 하기 때문에 특별하게 부른다.
이런 상황에서 쓰는 게 가장 흔할 것이다.
하나의 LAN에서 외부로 트래픽을 라우터를 거쳐서 보내는 상황이다.
이때 라우터는 나간 트래픽이 돌아올 수 있도록 출발 IP를 자신으로 바꾸고, 응답이 돌아왔을 때는 목적지 IP를 LAN 속 호스트로 바꿀 것이다.
이러한 작업 전반을 마스커레이딩이라 부르고, 이렇게 트래픽이 나가는 게이트웨이를 Masq 게이트라고도 한다.
리눅스를 통해 마스커레이딩을 할 때는 기본적으로 넷필터를 통해 수행할 수 있다.
그래서 iptables에서도 보면 액션 중에 MASQ 동작이 따로 존재하는데, 대신 포워딩과 같은 커널 모듈로딩이 필요하다.
실리움의 마스커레이딩
실리움에서는 이러한 마스커레이딩 동작에 대한 커스텀 기능을 지원한다.
기본적인 실리움의 동작은 클러스터를 떠난다 싶은 모든 트래픽에 대해 마스커레이딩을 하는 것이다.
이건 어딜 가든 사실 매우 당연한 동작이긴 하나, 만약 사내망과 같은 상황에서 라우팅 경로 설정이 이미 잘 돼 있고 또한 파드의 IP를 보존하는 것이 보안 요건 상 요구되는 것이라면 이런 것들을 커스텀할 수 있는 것이다.
마스커레이딩도 두 가지 모드가 있는데 하나는 위에서 말한 넷필터, 즉 iptables 기반으로 하는 것이다.
이 방식이 가장 기본적이라 할 수 있는데 문서에서는 레거시라 표현하며, 자신들이 만든 ebpf 모드도 같이 소개한다.
기본 실습
이번에도 기본 설정으로 bpf 기반 마스커레이딩을 활성화해둔 상태이니 어떤지 체크만 하고 간다.
kubectl -n kube-system exec ds/cilium -- cilium-dbg status | grep Masquerading
현재 ebpf 기반의 마스커레이딩이 eth0, eth1에 대해 활성화돼있으며, 파드 IP 대역으로 나가는 트래픽에 대해서는 마스커레이딩을 진행하지 않도록 설정돼있다.
(멀티 풀 관련 세팅하다가 10.244쪽 대역으로 설정이 돼버렸다.)
해당 대역은 ipv4-native-routing-cidr
값으로 설정된 것으로 네이티브 라우팅을 위한 설정값을 그대로 읽어서 마스커레이딩 제외 구간으로 활용하는 것이다.[3]
같은 대역에서는 마스커레이딩이 되지 않는다는 걸 이전 실습에서 충분히 확인할 수 있었다.
그렇다면 위에서 설정한 라우터로의 통신은 어떻게 될까?
kubectl exec -it curl-pod -- ping 192.168.10.200
kubectl exec -it curl-pod -- curl -s 10.10.1.200
# k8s-ctr 실행
hubble observe -f --pod curl-pod --ip-translation=false
# router에서 실행
tcpdump -i eth1 port 80 -nn
라우터로 사용한 vm은 노드와 같은 대역으로 192 대역을 사용하고, 더미 인터페이스를 두어 10.10대역에 대해서도 ip를 할당받았다.
그리고 각 노드에서는 해당 대역에 대해 라우터 vm으로 통하게 만들어두었다.
이 상태에서 트래픽을 보내는 파드가 있는 노드에서도, 실제 트래픽을 받은 라우터에서도 트래픽을 측정해본다.
트래픽을 보내는 측에서는 나가기 전까지는 파드의 ip가 그대로 유지된다.
그러나 실제로 트래픽이 나가는 순간에는 bpf 마스커레이딩이 적용된다.
그래서 라우터가 트래픽을 받을 때는 192.169.10.100, 즉 노드의 IP가 보이게 된다.
현재 설정 상 파드 IP 대역에 대해 라우터 vm에 경로 설정을 하지 않았으므로 마스커레이딩이 없다면 트래픽은 제대로 돌아오지 못하게 될 것이다.
ip-masq-agent
여기에 추가적인 내용으로, ebpf 모드 마스커레이딩은 ip-masq-agent의 기능도 지원한다.
쿠버네티스에서는 세부적인 범위에 대해서, 특정 IP만 구체적으로 마스커레이딩을 설정하고 싶다던가 하는 수요를 위해 ip-masq-agent라는 별도의 에이전트를 제공하고 있다.[4]
여기에서는 댜음의 설정을 통해 구체적인 마스커레이딩 커스텀이 가능하다.
- nonMasqueradeCIDRS - 마스커레이딩을 하지 않을 대역 리스트를 지정한다.
- masqLinkLocal ,maskLinkLocalIpv6 - Link Local를 마스커레이딩 할지 말지 설정한다.
- Link Local은 Ipv4 기준
169.254.0.0/16
을 말하는데 DHCP 무응답, IP 설정 실패 등일 때 자동으로 할당되는 주소이다(RFC 3927). - 해당 대역은 라우팅되지 않는 것이 보장되기에, 브로드캐스트 도메인 내부에서만 사용할 수 있다.
- Link Local은 Ipv4 기준
실리움은 이걸 또 ebpf 기반으로 만들어서 제공하고 있는 것이다.
다음의 상황에서 유용하게 사용할 수 있을 것으로 보인다.
- 클러스터 외부에 있는 사설 네트워크와 통신할 때
- 클러스터를 여러 개 구성해서 상호 통신할 때
- 운영 환경에서 네트워크 보안 정책상 NAT가 발생하지 않도록 요구하는 경우
실습
간단하게 활성화하고, 대역을 지정해서 넣어주면 된다.
helm upgrade cilium cilium/cilium --namespace kube-system --reuse-values \
--set ipMasqAgent.enabled=true --set ipMasqAgent.config.nonMasqueradeCIDRs='{10.10.1.0/24,10.10.2.0/24}'
해당 세팅은 컨피그맵으로 들어가게 되며, 이 값에 대한 변경은 자동으로 에이전트에 반영될 것이다.
이 상태에서 위에서 날린 패킷을 그대로 똑같이 날려보면..
트래픽은 분명히 날아온다.
그러나 라우터 측의 캐핏을 보면 알 수 있듯이 정말 파드의 ip가 마스커레이딩되지 않고 그대로 날아오며 이에 따라 응답이 돌아갈 곳이 없어 제대로 된 통신이 진행되지는 않는다.
아예 프로토콜을 한정시키지 않고 패킷을 따면 상황이 더 확실해진다.
라우터는 패킷을 받긴 했는데 출발지가 이상하게 적혀있으니 돌려줄 것으로 몰라서 계속 arp로 여기가 어디냐 소리없는 아우성을 외친다..
이걸 처리하고 싶다면 트래픽을 보내는 측(k8s-ctr)에서 해당 ip가 내 ip다 이야기할 수 있는 상태가 돼야 한다.
ip a add 10.244.0.99 dev eth1
(10.249는 오타..)
이러면 arp 요청에 대해 해당 인스턴스가 자신의 MAC 주소를 밝히게 되고, 결과적으로 응답이 정상적으로 돌아갈 수 있게 된다.
IP fragment handling
기본 기능으로서 마지막으로 볼 것은 IP 조각 처리이다.[5]
이건 기본 기능이라기보단.. 그냥 패킷을 조금 더 효율적으로 보내는 기술에 대한 개념이다.
이것 역시 현재 베타인 기능이다.
TCP의 경우 모든 데이터를 순서대로 확실하게 전달하기 위해 데이터를 쪼개(세그멘테이션) 번호를 붙여 전달하는 반면 UDP는 그냥 어련히 받겠거니 패킷을 던져버린다.
이때 UDP로 큰 데이터그램을 던지는 경우, L3인 IP 레벨에서 조각화(fragmentation)이 발생할 수 있으며, 이때 받는 측은 예상치 못한, 정말 신뢰할 수 없는 조각들을 받게 될 수도 있다.
큰 크기를 가진 트래픽을 전송할 때 실리움은 ebpf 데이터 경로를 구성해 조각 추적을 진행한다.
조각이 발생할 때 이를 bpf map으로 관리하면서 조각화된 데이터그램 단위가 제대로 전송될 수 있도록 도와준다는 것이다.
이 부분도 실습을 통해 확인을 해보면 좋을 것 같은데 어떻게 큰 규모의 UDP 데이터그램을 만들어낼 수 있을지 아이디어가 부족해 이 부분은 당장은 문서 정리로만 남겨두고자 한다.
이 기능은 기본 활성화돼있는 상태이다.
실제 조각화가 발생하여 bpf에서 이를 처리하게 되면 다음의 메트릭이 노출될 것이다.
cilium_bpf_map_pressure{map_name="cilium_ipv4_frag_datagrams"}
결론
보면 볼수록 실리움은 ebpf를 쓰는 CNI라는 한 마디로 표현하기에는 매우 다양한 기능을 가지고 있고, 또 그러한 방향으로 나아가고 있는 것 같다.
그 다양한 기능이 문서에 잘 정리돼있는가 하면.. 그 부분은 좀 의구심이 들긴 하는데, 아무튼 각 기능들을 이해하려고 들다보니 트래픽을 어떻게 최적화를 할 수 있는가에 대한 식견이 조금 넓어지는 느낌이다.
이전 글, 다음 글
다른 글 보기
이름 | index | noteType | created |
---|---|---|---|
1W - 실리움 기본 소개 | 1 | published | 2025-07-19 |
1W - 클러스터 세팅 및 cni 마이그레이션 | 2 | published | 2025-07-19 |
1W - 기본 실리움 탐색 및 통신 확인 | 3 | published | 2025-07-19 |
2W - 허블 기반 모니터링 | 4 | published | 2025-07-26 |
2W - 프로메테우스와 그라파나를 활용한 모니터링 | 5 | published | 2025-07-26 |
3W - 실리움 기본 - IPAM | 6 | published | 2025-08-02 |
3W - 실리움 기본 - Routing, Masq, IP Frag | 7 | published | 2025-08-02 |
4W - 실리움 라우팅 모드 실습 - native, vxlan, geneve | 8 | published | 2025-08-09 |
4W - 실리움 로드밸런서 기능 - 서비스 IP, L2 | 9 | published | 2025-08-09 |
하위 문서
이름 | is-folder | index | noteType | created |
---|---|---|---|---|
1W - 실리움 기본 소개 | false | 1 | published | 2025-07-19 |
1W - 클러스터 세팅 및 cni 마이그레이션 | false | 2 | published | 2025-07-19 |
1W - 기본 실리움 탐색 및 통신 확인 | false | 3 | published | 2025-07-19 |
2W - 허블 기반 모니터링 | false | 4 | published | 2025-07-26 |
2W - 프로메테우스와 그라파나를 활용한 모니터링 | false | 5 | published | 2025-07-26 |
3W - 실리움 기본 - IPAM | false | 6 | published | 2025-08-02 |
3W - 실리움 기본 - Routing, Masq, IP Frag | false | 7 | published | 2025-08-02 |
4W - 실리움 라우팅 모드 실습 - native, vxlan, geneve | false | 8 | published | 2025-08-09 |
4W - 실리움 로드밸런서 기능 - 서비스 IP, L2 | false | 9 | published | 2025-08-09 |
관련 문서
지식 문서, EXPLAIN
이름5 | is-folder | 생성 일자 |
---|---|---|
설치 요구사항 | false | 2025-07-06 10:34 |
ebpf 동작 가이드 | false | 2025-07-06 10:49 |
Hubble | false | 2025-07-06 10:38 |
0주차 검증 | false | 2025-07-06 12:46 |
Cilium | false | 2025-06-15 23:42 |
기타 문서
Z0-연관 knowledge, Z1-트러블슈팅 Z2-디자인,설계, Z3-임시, Z5-프로젝트,아카이브, Z8,9-미분류,미완이름16 | 코드 | 타입 | 생성 일자 |
---|---|---|---|
4W - 실리움 로드밸런서 기능 - 서비스 IP, L2 | Z8 | published | 2025-08-09 21:56 |
4W - 실리움 라우팅 모드 실습 - native, vxlan, geneve | Z8 | published | 2025-08-09 20:22 |
1W - 실리움 기본 소개 | Z8 | published | 2025-07-19 22:44 |
실리움 1주차 | Z8 | topic | 2025-07-13 19:50 |
실리움 개괄 | Z8 | topic | 2025-07-19 11:00 |
기본 실리움 탐색 및 통신 확인 | Z8 | topic | 2025-07-19 23:05 |
클러스터 세팅 및 cni 마이그레이션 | Z8 | topic | 2025-07-19 10:13 |
실리움 2주차 | Z8 | topic | 2025-07-20 19:05 |
노드로컬 dns | Z8 | topic | 2025-08-02 12:57 |
실리움 3주차 | Z8 | topic | 2025-07-27 19:44 |
1W - 클러스터 세팅 및 cni 마이그레이션 | Z8 | published | 2025-07-19 23:38 |
3W - 실리움 기본 - IPAM | Z8 | published | 2025-08-02 19:48 |
1W - 기본 실리움 탐색 및 통신 확인 | Z8 | published | 2025-07-19 23:45 |
Cilium 공식 문서 핸즈온 스터디 | Z8 | published | 2025-07-05 20:47 |
2W - 프로메테우스와 그라파나를 활용한 모니터링 | Z8 | published | 2025-07-26 21:15 |
2W - 허블 기반 모니터링 | Z8 | published | 2025-07-26 21:08 |
참고
https://docs.cilium.io/en/stable/network/concepts/routing/ ↩︎
https://www.ibiblio.org/pub/linux/docs/howto/other-formats/html_single/IP-Masquerade-HOWTO.html#IPMASQ-INTRO1.1 ↩︎
https://docs.cilium.io/en/stable/network/concepts/masquerading/#ebpf-based ↩︎
https://kubernetes.io/docs/tasks/administer-cluster/ip-masq-agent/ ↩︎
https://docs.cilium.io/en/stable/network/concepts/fragmentation/ ↩︎